# coding=utf-8
import logging, re

logger = logging.getLogger('com.metaist.heblib.HebrewWord')

# Defines the vowels used for counting syllables.
VOWELS = ['chirik', 'chirik-malei', 'tzerei', 'segol',
  'patach', 'patach-gnuvah', 'kamatz', 'cholom-chaser',
  'cholom-malei', 'kubutz', 'shuruk',
  'chataf-segol', 'chataf-patach', 'chataf-kamatz']

# Defines a parse tree for identifying Hebrew letters / vowels.
PARSE_RULES = {
  u'\u05b4': { # Chirik
    '': ['chirik'],
    u'\u05d9': ['chirik-malei']
  },
  u'\u05b5': ['tzerei'],
  u'\u05b6': ['segol'],
  u'\u05b7': ['patach'],
  u'\u05b8': ['kamatz'],
  u'\u05b9': ['cholom-chaser'],
  u'\u05bb': ['kubutz'],
  u'\u05b1': ['chataf-segol'],
  u'\u05b2': ['chataf-patach'],
  u'\u05b3': ['chataf-kamatz'],

  u'\u05b0': ["sh'va"],
  u'\u05bc': ['dagesh'],

  u'\u05d0': ['aleph'],
  u'\u05d1': {u'\u05bc': ['bet'], '': ['vet']},
  u'\u05d2': ['gimmel'],
  u'\u05d3': ['dalet'],
  u'\u05d4': { # Hey
    '': ['hey'],
    u'\u05bc': { # Dagesh
      '': ['hey', 'dagesh'],
      u'\u05b7': { # Patach
        '': ['hey', 'dagesh', 'patach'],
        None: ['hey', 'dagesh', 'patach-gnuvah']
      }
    }
  },
  u'\u05d5': { # Vav
    '': ['vav'],
    '`': {u'\u05b9': ['vav', 'cholom-chaser']},
    u'\u05b9': ['cholom-malei'],
    u'\u05bc': { # Dagesh
      '': ['shuruk'],
      u'\u05d5': { # Vav
        '': ['vav', 'dagesh', 'vav'],
        u'\u05bc': ['vav', 'dagesh', 'shuruk'],
        u'\u05b9': ['vav', 'dagesh', 'cholom-malei']
      },
      u'\u05b4': ['vav', 'dagesh', 'chirik'],
      u'\u05b5': ['vav', 'dagesh', 'tzerei'],
      u'\u05b6': ['vav', 'dagesh', 'segol'],
      u'\u05b7': ['vav', 'dagesh', 'patach'],
      u'\u05b8': ['vav', 'dagesh', 'kamatz'],
      u'\u05b9': ['vav', 'dagesh', 'cholom-chaser'],
      u'\u05bb': ['vav', 'dagesh', 'kubutz'],
      u'\u05b1': ['vav', 'dagesh', 'chataf-segol'],
      u'\u05b2': ['vav', 'dagesh', 'chataf-patach'],
      u'\u05b3': ['vav', 'dagesh', 'chataf-kamatz']
    }
  },
  u'\u05d6': ["za'in"],
  u'\u05d7': { # Chet
    '': ['chet'],
    u'\u05b7': { # Patach
      '': ['chet', 'patach'],
      None: ['chet', 'patach-gnuvah']
    }
  },
  u'\u05d8': ['tet'],
  u'\u05d9': ['yud'],
  u'\u05da': {u'\u05bc': ['kaf-sofit'], '': ['chaf-sofit']},
  u'\u05db': {u'\u05bc': ['kaf'], '': ['chaf']},
  u'\u05dc': ['lamed'],
  u'\u05dd': ['mem-sofit'],
  u'\u05de': ['mem'],
  u'\u05df': ['nun-sofit'],
  u'\u05e0': ['nun'],
  u'\u05e1': ['samech'],
  u'\u05e2': {
    '': ['ayin'],
    u'\u05b7': { # Patach
      '': ['ayin', 'patach'],
      None: ['ayin', 'patach-gnuvah']
    }
  },
  u'\u05e3': ['fey-sofit'],
  u'\u05e4': {u'\u05bc': ['pey'], '': ['fey']},
  u'\u05e5': ['tzadi-sofit'],
  u'\u05e6': ['tzadi'],
  u'\u05e7': ['kuf'],
  u'\u05e8': ['reish'],
  u'\u05e9': { # Shin
    '': ['sin'],
    u'\u05c1': ['shin'],
    u'\u05c2': ['sin'],
    u'\u05bc': {u'\u05c1': ['shin', 'dagesh'], u'\u05c2': ['sin', 'dagesh']}
  },
  u'\u05ea': {u'\u05bc': ['taf'], '': ['saf']},
  '': ['?']
}

# Defines the names of HaShem that should not be printed without care.
HASHEM_NAMES = [
  re.compile(u'א(ֱ)?ל(ו)?ֹה', re.X),           # Shem Elokah
  re.compile(u'א(.)?ד(ו)?ֹנ[ָ|ַ]י$', re.X), # Shem Adnuth
  re.compile(u'י(ְ)?הו[ָ|ִ]ה', re.X),       # Shem HaVayah
  re.compile(u'([^י]|^)שׁ[ַ|ָ]ד(ּ)?[ָ|ַ]י$', re.X), # Shakai
  re.compile(u'^אֵל(.)?$', re.X),   # Kel
  re.compile(u'^יָהּ$', re.X),      # Kah
  re.compile(u'^צְבָאוֹת$', re.X)   # Tzvakot
  ]

logger = logging.getLogger('com.metaist.heblib.HebrewWord')

def parse(str):
  '''Returns an array of letters for a unicode string.'''
  
  str = str.replace('&zwj;', '`') # Convert zero-width join into a single char.
  result = []
  trail = []
  idx = 0
  for char in str:
    char = str[idx]
    char_next = None # Indicates end of string.
    if idx + 1 < len(str):
      idx += 1
      char_next = str[idx]

    trail.append(char)
    rule = PARSE_RULES
    for step in trail:
      if step in rule: rule = rule[step] # Follow branch.
      elif '' in rule: break # There is a default rule.
      else: # There is no rule.
        rule = None
        break

    logger.debug('looking at ' + repr(char) + ' ' + repr(char_next))
    if type(rule) is list: # Found a leaf.
      logger.debug('found leaf ' + repr(rule)+ ' for ' + repr(char))
      result += rule
      trail = []
    elif type(rule) is dict: # Found a branch.
      logger.debug('found branch for ' + repr(char))
      if char_next is None and char_next in rule: # Special rule at the end of a word.
        logger.debug('found terminal leaf ' + repr(rule[None]) + ' for ' + repr(char))
        result += rule[None]
        trail = []
      elif char_next is None or char_next not in rule: # We need to find a default.
        if '' in rule: # There's a default leaf.
          logger.debug('found default leaf ' + repr(rule['']) + ' for ' + repr(char))
          result += rule['']
          trail = []
        else: # No idea what we've got.
          logger.debug('no rule for ' + repr(char))
          result += ['(?)']
          trail = []
    else:
      logger.debug('no rule[x2] for ' + repr(char))
      result += ['(?)']
      trail = []

  return result

def syllables(letters):
  '''Returns the number of syllables (informally counted) in an array of letters.'''
  
  result = 0
  for letter in letters:
    if letter in VOWELS:
      result += 1

  return result

def hashem_name(str):
  '''Returns True if the string contains a name of HaShem that should not be printed.'''
  
  result = False
  for rule in HASHEM_NAMES:
    # NOTE: We use .search() rather than .match() to look 
    # anywhere within the string.
    if rule.search(str) is not None: return True

  return result
